﻿Imports System
Imports System.CodeDom
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Text

Namespace System.Workflow.Activities.Rules
    Friend Class RuleDecompiler
        ' Methods
        Private Shared Sub AppendCharacter(ByVal decompilation As StringBuilder, ByVal charValue As Char, ByVal quoteCharacter As Char)
            If (charValue = quoteCharacter) Then
                decompilation.Append("\")
                decompilation.Append(quoteCharacter)
            ElseIf (charValue = "\"c) Then
                decompilation.Append("\\")
            ElseIf (((charValue >= " "c) AndAlso (charValue < ""c)) OrElse (Char.IsLetterOrDigit(charValue) OrElse Char.IsPunctuation(charValue))) Then
                decompilation.Append(charValue)
            Else
                Dim str As String = Nothing
                Select Case charValue
                    Case ChrW(0)
                        str = "\0"
                        Exit Select
                    Case ChrW(7)
                        str = "\a"
                        Exit Select
                    Case ChrW(8)
                        str = "\b"
                        Exit Select
                    Case ChrW(9)
                        str = "\t"
                        Exit Select
                    Case ChrW(10)
                        str = "\n"
                        Exit Select
                    Case ChrW(11)
                        str = "\v"
                        Exit Select
                    Case ChrW(12)
                        str = "\f"
                        Exit Select
                    Case ChrW(13)
                        str = "\r"
                        Exit Select
                End Select
                If (Not str Is Nothing) Then
                    decompilation.Append(str)
                Else
                    decompilation.Append("\u")
                    Dim num As UInt16 = charValue
                    Dim i As Integer = 12
                    Do While (i >= 0)
                        Dim num3 As Integer = (CInt(15) << i)
                        Dim num4 As Byte = CByte(((num And num3) >> i))
                        decompilation.Append("0123456789ABCDEF".Chars(num4))
                        i = (i - 4)
                    Loop
                End If
            End If
        End Sub

        Private Shared Sub DecompileCharacterLiteral(ByVal decompilation As StringBuilder, ByVal charValue As Char)
            decompilation.Append("'")
            RuleDecompiler.AppendCharacter(decompilation, charValue, "'"c)
            decompilation.Append("'")
        End Sub

        Private Shared Sub DecompileFloatingPointLiteral(ByVal decompilation As StringBuilder, ByVal value As Object, ByVal suffix As Char)
            Dim str As String = Convert.ToString(value, CultureInfo.InvariantCulture)
            decompilation.Append(str)
            If (suffix = "d"c) Then
                Dim flag As Boolean = (str.IndexOf("."c) >= 0)
                Dim flag2 As Boolean = (str.IndexOfAny(New Char() { "e"c, "E"c }) >= 0)
                If (Not flag AndAlso Not flag2) Then
                    decompilation.Append(".0")
                End If
            Else
                decompilation.Append(suffix)
            End If
        End Sub

        Friend Shared Function DecompileMethod(ByVal method As MethodInfo) As String
            Dim str As String
            If (method Is Nothing) Then
                Return String.Empty
            End If
            Dim decompilation As New StringBuilder
            RuleDecompiler.DecompileType_Helper(decompilation, method.DeclaringType)
            decompilation.Append("."c)
            If RuleDecompiler.knownOperatorMap.TryGetValue(method.Name, str) Then
                decompilation.Append(str)
            Else
                decompilation.Append(method.Name)
            End If
            decompilation.Append("("c)
            Dim parameters As ParameterInfo() = method.GetParameters
            Dim i As Integer
            For i = 0 To parameters.Length - 1
                RuleDecompiler.DecompileType_Helper(decompilation, parameters(i).ParameterType)
                If (i <> (parameters.Length - 1)) Then
                    decompilation.Append(", ")
                End If
            Next i
            decompilation.Append(")"c)
            Return decompilation.ToString
        End Function

        Friend Shared Sub DecompileObjectLiteral(ByVal decompilation As StringBuilder, ByVal primitiveValue As Object)
            If (primitiveValue Is Nothing) Then
                decompilation.Append("null")
            Else
                Dim type As Type = primitiveValue.GetType
                If (type Is GetType(String)) Then
                    RuleDecompiler.DecompileStringLiteral(decompilation, CStr(primitiveValue))
                ElseIf (type Is GetType(Char)) Then
                    RuleDecompiler.DecompileCharacterLiteral(decompilation, DirectCast(primitiveValue, Char))
                ElseIf (type Is GetType(Long)) Then
                    RuleDecompiler.DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "L")
                ElseIf (type Is GetType(UInt32)) Then
                    RuleDecompiler.DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "U")
                ElseIf (type Is GetType(UInt64)) Then
                    RuleDecompiler.DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "UL")
                ElseIf (type Is GetType(Single)) Then
                    RuleDecompiler.DecompileFloatingPointLiteral(decompilation, primitiveValue, "f"c)
                ElseIf (type Is GetType(Double)) Then
                    RuleDecompiler.DecompileFloatingPointLiteral(decompilation, primitiveValue, "d"c)
                ElseIf (type Is GetType(Decimal)) Then
                    RuleDecompiler.DecompileFloatingPointLiteral(decompilation, primitiveValue, "m"c)
                Else
                    decompilation.Append(primitiveValue.ToString)
                End If
            End If
        End Sub

        Private Shared Sub DecompileStringLiteral(ByVal decompilation As StringBuilder, ByVal strValue As String)
            decompilation.Append("""")
            Dim i As Integer
            For i = 0 To strValue.Length - 1
                Dim c As Char = strValue.Chars(i)
                If ((Char.IsHighSurrogate(c) AndAlso ((i + 1) < strValue.Length)) AndAlso Char.IsLowSurrogate(strValue.Chars((i + 1)))) Then
                    decompilation.Append(c)
                    i += 1
                    decompilation.Append(strValue.Chars(i))
                Else
                    RuleDecompiler.AppendCharacter(decompilation, c, """"c)
                End If
            Next i
            decompilation.Append("""")
        End Sub

        Private Shared Sub DecompileSuffixedIntegerLiteral(ByVal decompilation As StringBuilder, ByVal value As Object, ByVal suffix As String)
            decompilation.Append(value.ToString)
            decompilation.Append(suffix)
        End Sub

        Friend Shared Function DecompileType(ByVal type As Type) As String
            If (type Is Nothing) Then
                Return String.Empty
            End If
            Dim decompilation As New StringBuilder
            RuleDecompiler.DecompileType_Helper(decompilation, type)
            Return decompilation.ToString
        End Function

        Friend Shared Sub DecompileType(ByVal decompilation As StringBuilder, ByVal typeRef As CodeTypeReference)
            Dim str As String = RuleDecompiler.UnmangleTypeName(typeRef.BaseType)
            decompilation.Append(str)
            If ((Not typeRef.TypeArguments Is Nothing) AndAlso (typeRef.TypeArguments.Count > 0)) Then
                decompilation.Append("<")
                Dim flag As Boolean = True
                Dim reference As CodeTypeReference
                For Each reference In typeRef.TypeArguments
                    If Not flag Then
                        decompilation.Append(", ")
                    End If
                    flag = False
                    RuleDecompiler.DecompileType(decompilation, reference)
                Next
                decompilation.Append(">")
            End If
            If (typeRef.ArrayRank > 0) Then
                Do
                    decompilation.Append("[")
                    Dim i As Integer
                    For i = 1 To typeRef.ArrayRank - 1
                        decompilation.Append(",")
                    Next i
                    decompilation.Append("]")
                    typeRef = typeRef.ArrayElementType
                Loop While (typeRef.ArrayRank > 0)
            End If
        End Sub

        Private Shared Sub DecompileType_Helper(ByVal decompilation As StringBuilder, ByVal type As Type)
            If type.HasElementType Then
                RuleDecompiler.DecompileType_Helper(decompilation, type.GetElementType)
                If type.IsArray Then
                    decompilation.Append("[")
                    decompilation.Append(","c, (type.GetArrayRank - 1))
                    decompilation.Append("]")
                ElseIf type.IsByRef Then
                    decompilation.Append("&"c)
                ElseIf type.IsPointer Then
                    decompilation.Append("*"c)
                End If
            Else
                Dim fullName As String = type.FullName
                If (fullName Is Nothing) Then
                    fullName = type.Name
                End If
                fullName = RuleDecompiler.UnmangleTypeName(fullName)
                decompilation.Append(fullName)
                If type.IsGenericType Then
                    decompilation.Append("<")
                    Dim genericArguments As Type() = type.GetGenericArguments
                    RuleDecompiler.DecompileType_Helper(decompilation, genericArguments(0))
                    Dim i As Integer
                    For i = 1 To genericArguments.Length - 1
                        decompilation.Append(", ")
                        RuleDecompiler.DecompileType_Helper(decompilation, genericArguments(i))
                    Next i
                    decompilation.Append(">")
                End If
            End If
        End Sub

        Private Shared Function GetBinaryPrecedence(ByVal expression As CodeExpression) As Operation
            Dim expression2 As CodeBinaryOperatorExpression = DirectCast(expression, CodeBinaryOperatorExpression)
            Select Case expression2.[Operator]
                Case CodeBinaryOperatorType.Add, CodeBinaryOperatorType.Subtract
                    Return Operation.Additive
                Case CodeBinaryOperatorType.Multiply, CodeBinaryOperatorType.Divide, CodeBinaryOperatorType.Modulus
                    Return Operation.Multiplicative
                Case CodeBinaryOperatorType.IdentityInequality, CodeBinaryOperatorType.IdentityEquality, CodeBinaryOperatorType.ValueEquality
                    Return Operation.Equality
                Case CodeBinaryOperatorType.BitwiseOr
                    Return Operation.BitwiseOr
                Case CodeBinaryOperatorType.BitwiseAnd
                    Return Operation.BitwiseAnd
                Case CodeBinaryOperatorType.BooleanOr
                    Return Operation.LogicalOr
                Case CodeBinaryOperatorType.BooleanAnd
                    Return Operation.LogicalAnd
                Case CodeBinaryOperatorType.LessThan, CodeBinaryOperatorType.LessThanOrEqual, CodeBinaryOperatorType.GreaterThan, CodeBinaryOperatorType.GreaterThanOrEqual
                    Return Operation.Comparitive
            End Select
            Dim exception As New NotSupportedException(String.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, New Object() { expression2.[Operator].ToString }))
            exception.Data.Item("ErrorObject") = expression2
            Throw exception
        End Function

        Private Shared Function GetCastPrecedence(ByVal expression As CodeExpression) As Operation
            Return Operation.Unary
        End Function

        Private Shared Function GetPostfixPrecedence(ByVal expression As CodeExpression) As Operation
            Return Operation.Postfix
        End Function

        Private Shared Function GetPrecedence(ByVal expression As CodeExpression) As Operation
            Dim precedence As ComputePrecedence
            Dim noParentheses As Operation = Operation.NoParentheses
            If RuleDecompiler.precedenceMap.TryGetValue(expression.GetType, precedence) Then
                noParentheses = precedence.Invoke(expression)
            End If
            Return noParentheses
        End Function

        Private Shared Function InitializeKnownOperatorMap() As Dictionary([Of] String, String)
            Dim dictionary As New Dictionary([Of] String, String)(&H1B)
            dictionary.Add("op_UnaryPlus", "operator +")
            dictionary.Add("op_UnaryNegation", "operator -")
            dictionary.Add("op_OnesComplement", "operator ~")
            dictionary.Add("op_LogicalNot", "operator !")
            dictionary.Add("op_Increment", "operator ++")
            dictionary.Add("op_Decrement", "operator --")
            dictionary.Add("op_True", "operator true")
            dictionary.Add("op_False", "operator false")
            dictionary.Add("op_Implicit", "implicit operator")
            dictionary.Add("op_Explicit", "explicit operator")
            dictionary.Add("op_Equality", "operator ==")
            dictionary.Add("op_Inequality", "operator !=")
            dictionary.Add("op_GreaterThan", "operator >")
            dictionary.Add("op_GreaterThanOrEqual", "operator >=")
            dictionary.Add("op_LessThan", "operator <")
            dictionary.Add("op_LessThanOrEqual", "operator <=")
            dictionary.Add("op_Addition", "operator +")
            dictionary.Add("op_Subtraction", "operator -")
            dictionary.Add("op_Multiply", "operator *")
            dictionary.Add("op_Division", "operator /")
            dictionary.Add("op_IntegerDivision", "operator \")
            dictionary.Add("op_Modulus", "operator %")
            dictionary.Add("op_LeftShift", "operator <<")
            dictionary.Add("op_RightShift", "operator >>")
            dictionary.Add("op_BitwiseAnd", "operator &")
            dictionary.Add("op_BitwiseOr", "operator |")
            dictionary.Add("op_ExclusiveOr", "operator ^")
            Return dictionary
        End Function

        Private Shared Function InitializeKnownTypeMap() As Dictionary([Of] String, String)
            Dim dictionary As New Dictionary([Of] String, String)
            dictionary.Add("System.Char", "char")
            dictionary.Add("System.Byte", "byte")
            dictionary.Add("System.SByte", "sbyte")
            dictionary.Add("System.Int16", "short")
            dictionary.Add("System.UInt16", "ushort")
            dictionary.Add("System.Int32", "int")
            dictionary.Add("System.UInt32", "uint")
            dictionary.Add("System.Int64", "long")
            dictionary.Add("System.UInt64", "ulong")
            dictionary.Add("System.Single", "float")
            dictionary.Add("System.Double", "double")
            dictionary.Add("System.Decimal", "decimal")
            dictionary.Add("System.Boolean", "bool")
            dictionary.Add("System.String", "string")
            dictionary.Add("System.Object", "object")
            dictionary.Add("System.Void", "void")
            Return dictionary
        End Function

        Private Shared Function InitializePrecedenceMap() As Dictionary([Of] Type, ComputePrecedence)
            Dim dictionary As New Dictionary([Of] Type, ComputePrecedence)(7)
            dictionary.Add(GetType(CodeBinaryOperatorExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetBinaryPrecedence))
            dictionary.Add(GetType(CodeCastExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetCastPrecedence))
            dictionary.Add(GetType(CodeFieldReferenceExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetPostfixPrecedence))
            dictionary.Add(GetType(CodePropertyReferenceExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetPostfixPrecedence))
            dictionary.Add(GetType(CodeMethodInvokeExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetPostfixPrecedence))
            dictionary.Add(GetType(CodeObjectCreateExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetPostfixPrecedence))
            dictionary.Add(GetType(CodeArrayCreateExpression), New ComputePrecedence(AddressOf RuleDecompiler.GetPostfixPrecedence))
            Return dictionary
        End Function

        Friend Shared Function MustParenthesize(ByVal childExpr As CodeExpression, ByVal parentExpr As CodeExpression) As Boolean
            If (parentExpr Is Nothing) Then
                Return False
            End If
            Dim precedence As Operation = RuleDecompiler.GetPrecedence(childExpr)
            Dim operation2 As Operation = RuleDecompiler.GetPrecedence(parentExpr)
            If (operation2 = precedence) Then
                Dim expression As CodeBinaryOperatorExpression = [TryCast](parentExpr,CodeBinaryOperatorExpression)
                Return ((Not expression Is Nothing) AndAlso (childExpr Is expression.Right))
            End If
            Return (operation2 > precedence)
        End Function

        Private Shared Function TryReplaceKnownTypes(ByVal typeName As String) As String
            Dim str As String = Nothing
            If Not RuleDecompiler.knownTypeMap.TryGetValue(typeName, str) Then
                str = typeName
            End If
            Return str
        End Function

        Private Shared Function UnmangleTypeName(ByVal typeName As String) As String
            Dim index As Integer = typeName.IndexOf("`"c)
            If (index > 0) Then
                typeName = typeName.Substring(0, index)
            End If
            typeName = typeName.Replace("+"c, "."c)
            typeName = RuleDecompiler.TryReplaceKnownTypes(typeName)
            Return typeName
        End Function


        ' Fields
        Private Shared knownOperatorMap As Dictionary([Of] String, String) = RuleDecompiler.InitializeKnownOperatorMap
        Private Shared knownTypeMap As Dictionary([Of] String, String) = RuleDecompiler.InitializeKnownTypeMap
        Private Shared precedenceMap As Dictionary([Of] Type, ComputePrecedence) = RuleDecompiler.InitializePrecedenceMap

        ' Nested Types
        Private Delegate Function ComputePrecedence(ByVal expresssion As CodeExpression) As Operation

        Private Enum Operation
            ' Fields
            Additive = 7
            BitwiseAnd = 4
            BitwiseOr = 3
            Comparitive = 6
            Equality = 5
            LogicalAnd = 2
            LogicalOr = 1
            Multiplicative = 8
            NoParentheses = 11
            Postfix = 10
            RootExpression = 0
            Unary = 9
        End Enum
    End Class
End Namespace

